#include <GL/glew.h>
#include "Map.hpp"

Tile* Map::getTile()
{
  randTile = rand()%5;
  if(randTile == 0)
    return dynamic_cast<Tile*>(new Crossroads());
  else if(randTile == 1)
    return dynamic_cast<Tile*>(new Troads());
  else if(randTile == 2)
    return dynamic_cast<Tile*>(new Alley());
  else if(randTile == 3)
    return dynamic_cast<Tile*>(new Angleroads());
  else if(randTile == 4)
    return dynamic_cast<Tile*>(new Noroad());
  return NULL;
}


Map::Map(Architect* a, int s)
  : archi(a), size(s)
{
  tiles = new vector<Tile*>();
  lods = new vector<LOD*>();
  //Occlusion testing
  occlusion = isOcclusionAvble();
  for(int i=0; i<size; i++)
  {
    for(int j=0; j<size; j++)
    {
      Tile* t = getTile();
      if(tiles->size()==0)
	addTile(t);
      else
      {
	while(!testAndSetTile(t, i, j))
	{
	  delete t;
	  t = getTile();
	}
	addTile(t);
      }
    }
  }
}

Map::~Map()
{
  delete tiles;
}

bool Map::testAndSetTile(Tile* t, int i, int j)
{
  bool top = false;
  bool left = false;
  for(int turn=0; turn<3; turn++)
  {
    if(i!=0) // bottom border
    {
      // test with bottom tile
      if(tiles->at(tiles->size()-size)->north == t->south)
	top = true;
      else
	top = false;
    }
    else top = true;
    
    if(j!=0) // left border
    {
      // test with left tile
      if(tiles->at(tiles->size()-1)->east == t->west)
	left = true;
      else
	left = false;
    }
    else left = true;
    
    if(left && top)
      return true;
    
    t->turnLeft();
  }
  return false;
}

void Map::addTile(Tile* t)
{
  t->setArchitect(archi);
  int style = rand()%3;
  if(style == 0) // Business
  {
    int type = rand()%4;
    if(randTile == 4 && type==0)
    {
      archi->workWithPlan("Buildings/Assets/Tile/ESBDistrict.xml");
      t->set(40, 50);
    }
    else if(randTile == 4 && type==1) // Factory
    {
      archi->workWithPlan("Buildings/Assets/Tile/PrisonDistrict.xml");
      t->set(7, 12);
    }
    else
    {
      archi->workWithPlan("Buildings/Assets/Tile/BusinessDistrict.xml");
      t->set(10, 20);
    }
  }
  else if(style == 1) // Poor
  {
    archi->workWithPlan("Buildings/Assets/Tile/PoorDistrict.xml");
    t->set(0, 5);
  }
  else if(style == 2)
  {
    archi->workWithPlan("Buildings/Assets/Tile/RichDistrict.xml");
    t->set(5, 10);
  }
  int x = (int)t->getSize()*(tiles->size()%size);
  int y = (int)(t->getSize()*(tiles->size()/size));
  t->translateOf(x, y, 0);
  t->translateOf(-size*50/2, -size*50/2, 0);
  tiles->push_back(t);
}

void Map::draw() const
{
  int groundSize = size*50;
  for(unsigned int i=0; i<lods->size(); i++)
    lods->at(i)->calculate();

  glPushMatrix();
  /* QUAD */
  glBegin(GL_QUADS);
    glNormal3f(0, 0, 1);
    glVertex3i(groundSize-groundSize/2, -groundSize/2, 0);
    glVertex3i(groundSize-groundSize/2, groundSize-groundSize/2, 0);
    glVertex3i(-groundSize/2, groundSize-groundSize/2, 0);
    glVertex3i(-groundSize/2, -groundSize/2, 0);
  glEnd();
  if(occlusion)
    drawWithOcclusion(tiles,1);
  else
  {
    for(unsigned int i=0; i<tiles->size(); i++)
      tiles->at(i)->draw();
  }
  glPopMatrix();
}


void Map::setLOD(const Camera& cam)
{
  Vector<float> tpos;
  for(unsigned int i=0; i<tiles->size(); i++)
  {
    tpos = tiles->at(i)->getPos();
    for(unsigned int j=0;
	j<tiles->at(i)->getAssets()->size();
	j++)
    {
      lods->push_back(new LOD(cam, 
	tiles->at(i)->getAssets()->at(j),
	tpos + tiles->at(i)->getAssets()->at(j)->getPos()));
    }
  }
}

void Map::setOcclusion(bool b)
{
  occlusion = b;
}

void Map::drawWithOcclusion(std::vector<Tile*>* v, int depth) const
{
  GLuint queries[v->size()];
  GLuint sampleCount;
  GLint available;

  glGenQueriesARB(v->size(), queries);
  // before this point, render major occluders
  glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
  glDepthMask(GL_FALSE);
  for (uint i = 0; i < v->size(); i++) {
      glBeginQueryARB(GL_SAMPLES_PASSED_ARB, queries[i]);
      v->at(i)->drawBVR(depth);//drawBVR draw to much bv, so to much query slow tio much the rendrering sequence
      glEndQueryARB(GL_SAMPLES_PASSED_ARB);
  }

  glFlush();

  unsigned int ioccl = v->size()*3/4; // instead of N-1, to prevent the GPU from going idle
  do {
      glGetQueryObjectivARB(queries[ioccl], GL_QUERY_RESULT_AVAILABLE_ARB, &available);
  } while (!available);

  glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
  glDepthMask(GL_TRUE);

  for (ioccl = 0; ioccl < v->size(); ioccl++) 
  {
      glGetQueryObjectuivARB(queries[ioccl], GL_QUERY_RESULT_ARB,&sampleCount);
      if (sampleCount > 50) //Can be used as parameter to display only real visible buildings
      {
	  v->at(ioccl)->draw();
      }
  }
}

bool Map::isOcclusionAvble()
{
  if (GLEW_ARB_occlusion_query)
  {
  //Occlusion query is supported on this hardware
    return true;
  }
  printf("Hardware do NOT support ARB Occlusion Qierying... It will be laggy...\n");
  return false;
}